import * as Cesium from '@cesiumjs';
import WrfboObject from "./WrfboObject";
import * as g3xUtils from "@g3x/utils";
import g3xExtends from "./extends";

const _0x3f7931 = new Cesium.PerspectiveFrustum();
const _0x2b0cb2 = new Cesium.PerspectiveOffCenterFrustum();
const _0x432f68 = new Cesium.OrthographicFrustum();
const _0x4a6679 = new Cesium.OrthographicOffCenterFrustum();

function executeCmd(cmd, scene, sceneContext, passState, _0x47f98d) {
  const _frameState = scene._frameState;
  if (Cesium.defined(scene.debugCommandFilter) && !scene.debugCommandFilter(cmd)) {
    return;
  }
  if (cmd instanceof Cesium.ClearCommand) {
    cmd.execute(sceneContext, passState);
    return;
  }
  if (_frameState.useLogDepth && Cesium.defined(cmd.derivedCommands.logDepth)) {
    cmd = cmd.derivedCommands.logDepth.command;
  }
  const passes = _frameState.passes;
  if (!passes.pick && scene._hdr && Cesium.defined(cmd.derivedCommands) && Cesium.defined(cmd.derivedCommands.hdr)) {
    cmd = cmd.derivedCommands.hdr.command;
  }
  if (passes.pick || passes.depth) {
    return;
  }
  if (scene.debugShowCommands || scene.debugShowFrustums) {
    return;
  }
  cmd.execute(sceneContext, passState);
}

var camera_ = undefined;
const passState_ = Cesium.Cesium3DTilePassState && new Cesium.Cesium3DTilePassState({
  'pass': Cesium.Cesium3DTilePass.RENDER
});
const updateFunc = Cesium.OctahedralProjectedCubeMap.prototype.update;

function updateDummy() {}

function preRenderFunc(scene, waterHeight, wrfbo) {
  if (!camera_) {
    camera_ = new Cesium.Camera(scene);
  }
  if (scene.mode !== Cesium.SceneMode.SCENE3D) {
    return;
  }
  const sceneContext = scene.context;
  const passState1 = scene._view.passState;
  const cameraCopy = Cesium.Camera.clone(scene.camera, camera_);
  const {
    longitude,
    latitude,
    height
  } = cameraCopy.positionCartographic;
  const {
    pitch,
    heading,
    roll
  } = cameraCopy;
  cameraCopy.setView({
    'destination': Cesium.Cartesian3.fromRadians(longitude, latitude, waterHeight + waterHeight - height),
    'orientation': {
      'heading': heading,
      'pitch': -pitch,
      'roll': roll
    }
  });
  const bufferWidth = scene["context"]['drawingBufferWidth'] * 0.5;
  const bufferHeight = scene['context']["drawingBufferHeight"] * 0.5;
  passState1["viewport"]['x'] = 0x0;
  passState1["viewport"]['y'] = 0x0;
  passState1['viewport']['width'] = bufferWidth;
  passState1['viewport']["height"] = bufferHeight;
  wrfbo.update(sceneContext, bufferWidth, bufferHeight);
  wrfbo["_colorTexture"];
  passState1.framebuffer = wrfbo._framebuffer;
  const clearColorCommand = scene["_clearColorCommand"];
  Cesium.Color.multiplyByScalar(Cesium.Color.DEEPSKYBLUE, 0.1, clearColorCommand.color);
  clearColorCommand.color.alpha = 0x1;
  clearColorCommand.execute(sceneContext, passState1);
  const depthClearCommand = scene['_depthClearCommand'];
  const uniformState = sceneContext["uniformState"];
  uniformState.updateCamera(cameraCopy);
  uniformState.xbsjWaterHeight = waterHeight;

  var frustumOpt;
  if (Cesium.defined(cameraCopy.frustum.fov)) {
    frustumOpt = cameraCopy.frustum.clone(_0x3f7931);
  } else if (Cesium.defined(cameraCopy.frustum.infiniteProjectionMatrix)) {
    frustumOpt = cameraCopy.frustum.clone(_0x2b0cb2);
  } else if (Cesium.defined(cameraCopy.frustum.width)) {
    frustumOpt = cameraCopy.frustum.clone(_0x432f68);
  } else {
    frustumOpt = cameraCopy.frustum.clone(_0x4a6679);
  }
  frustumOpt.near = cameraCopy.frustum.near;
  frustumOpt.far = cameraCopy.frustum.far;
  uniformState.updateFrustum(frustumOpt);
  const frameState = scene._frameState;
  frameState.passes.render = true;
  frameState.tilesetPassState = passState_;
  scene.frameState.commandList.length = 0; {
    Cesium.OctahedralProjectedCubeMap.prototype.update = updateDummy;
    scene._primitives.update(frameState);
    Cesium.OctahedralProjectedCubeMap.prototype.update = updateFunc;
  }
  scene._view.createPotentiallyVisibleSet(scene);
  const frustumCommandsList = scene._view.frustumCommandsList;
  const frustumCommandsListLength = frustumCommandsList.length;
  for (var i = 0; i < frustumCommandsListLength; ++i) {
    const _0x2763f1 = frustumCommandsListLength - i - 1;
    const cmd = frustumCommandsList[_0x2763f1];
    frustumOpt.near = _0x2763f1 !== 0x0 ? cmd.near * scene.opaqueFrustumNearOffset : cmd.near;
    frustumOpt.far = cmd.far;
    uniformState.updateFrustum(frustumOpt);
    depthClearCommand.execute(sceneContext, passState1);
    uniformState.updatePass(Cesium.Pass.CESIUM_3D_TILE);
    const cmds = cmd.commands[Cesium.Pass.CESIUM_3D_TILE];
    const nCmds = cmd.indices[Cesium.Pass.CESIUM_3D_TILE];
    for (var k = 0x0; k < nCmds; ++k) {
      executeCmd(cmds[k], scene, sceneContext, passState1);
    }
  }
  passState1.framebuffer = undefined;
  uniformState.xbsjWaterHeight = -0x1388;
}

// XbsjWaterManager
class G3xWaterManager {

  constructor(scene, options) {
    this._scene = scene;
    this._wrfbo = new WrfboObject();
    this._trackedPrimitives = [];
    this.initPreRenderEventListener(scene);
    this._createDebugView();
    this.debugShow(false);
  }

  initPreRenderEventListener(scene) {
    var _this = this;
    this._disposeListener = scene.preRender.addEventListener(() => {
      const camera = scene.camera;
      const {
        frustum,
        positionWC,
        directionWC,
        upWC
      } = camera;
      var cull = frustum.computeCullingVolume(positionWC, directionWC, upWC);
      var trackedPrimitive = _this._trackedPrimitives.some((e) => {
        if (!e.show) {
          return false;
        }
        var coords = e._boundingSphereWC || e._boundingVolumes;
        return coords && coords.some((e) => {
          return cull.computeVisibility(e) !== Cesium.Intersect.OUTSIDE;
        });
      });
      if (trackedPrimitive) {
        var _maxDis = Number.MAX_VALUE;
        var _maxDisObj = undefined;
        _this._trackedPrimitives.forEach((e) => {
          if (!e.show) {
            return;
          }
          var coords = e._boundingSphereWC || e._boundingVolumes;
          if (coords && coords.some((d) => {
              return cull.computeVisibility(d) !== Cesium.Intersect.OUTSIDE;
            })) {
            var _dis = Cesium.Cartesian3.distanceSquared(e.centerPos, camera.positionWC) - e.radius;
            if (_maxDis > _dis) {
              _maxDis = _dis;
              _maxDisObj = e;
            }
          }
        });
        if (_maxDisObj) {
          preRenderFunc(_this._scene, _maxDisObj.waterHeight, _this._wrfbo);
        }
      }
    });
  }
}

(0, g3xExtends)(G3xWaterManager);

export default G3xWaterManager;
